# OpenWrt sysupgrade 升级原理 OpenWrt的升级是使用sysupgrade工具来升级的,该工具为`/sbin/sysupgrade`脚本,可以附带很多参数,但是我们一般就直接使用命令`sysupgrade openwrt-ramips-mt7621-device-squashfs-sysupgrade.bin`升级 |参数 |说明 | |-------|-------------| |-i |开启交互模式| |-d |重启前延迟,延迟秒数是传进来的| |-v |会打印sysupgrade脚本中的一些信息,脚本中默认打印| |-q |与-v相反| |-n |升级后不保存配置,默认保存配置| |-c |保存所有的改动配置文件到/etc/| |-b |用sysupgrade.conf中指定的文件,创建.tar.gz格式备份文件| |-r |用上步创建的.tar.gz文件,恢复配置| |-l |列出将会备份的文件列表| |-f |从.tar.gz恢复配置| |-F |即使升级文件检测失败,也要升级,这个参数是危险的,慎用| |-T |验证升级文件和.tar.gz配置文件,但不升级| |-h |打印帮助信息| ## 脚本分析 sysupgrade的升级流程查看`/sbin/sysupgrade`脚本 1. check校验固件合法性 sysupgrade的第一步就是校验固件合法性,如下判断platform_check_image函数存在则开始执行。 ```bash type platform_check_image >/dev/null 2>/dev/null || { echo "Firmware upgrade is not implemented for this platform." exit 1 } for check in $sysupgrade_image_check; do ( eval "$check \"\$1\"" ) || { echo "Image check '$check' $1 failed." return 1 } done ``` 该函数位于`lib/upgrade/platform.sh`中,根据产品的型号来判断各自的magic值。 ```bash platform_check_image() { local board=$(ramips_board_name) local magic="$(get_magic_long "$1")" [ "$#" -gt 1 ] && return 1 case "$board" in br-6475nd) [ "$magic" != "43535953" ] && { echo "Invalid image type." return 1 } return 0 ;; ubnt-erx) nand_do_platform_check "$board" "$1" return $?; ;; mt7621-*) [ "$magic" != "d00dfeed" ] && { echo "Invalid image type." return 1 } return 0; ;; esac echo "Sysupgrade is not yet supported on $board." return 1 } ``` 我们一般会在该check函数下面添加私有的校验方式,如固件的验签等功能。 2. 保存config配置文件 ```bash export SAVE_CONFIG=1 ``` 默认的`SAVE_CONFIG`标志为为1,所以默认要保存uci配置文件 ```bash if [ -n "$CONF_IMAGE" ]; then case "$(get_magic_word $CONF_IMAGE cat)" in # .gz files 1f8b) ;; *) echo "Invalid config file. Please use only .tar.gz files" exit 1 ;; esac get_image "$CONF_IMAGE" "cat" > "$CONF_TAR" export SAVE_CONFIG=1 elif ask_bool $SAVE_CONFIG "Keep config files over reflash"; then [ $TEST -eq 1 ] || do_save_conffiles export SAVE_CONFIG=1 else export SAVE_CONFIG=0 fi ``` 保存config文件函数如下: ```bash do_save_conffiles() { local conf_tar="${1:-$CONF_TAR}" [ -z "$(rootfs_type)" ] && { echo "Cannot save config while running from ramdisk." ask_bool 0 "Abort" && exit return 0 } run_hooks "$CONFFILES" $sysupgrade_init_conffiles ask_bool 0 "Edit config file list" && vi "$CONFFILES" v "Saving config files..." [ "$VERBOSE" -gt 1 ] && TAR_V="v" || TAR_V="" tar c${TAR_V}zf "$conf_tar" -T "$CONFFILES" 2>/dev/null rm -f "$CONFFILES" } ``` 如果sysupgrade的时候带参数-n,`sysupgrade -n openwrt-ramips-mt7621-device-squashfs-sysupgrade. bin`,则会将`SAVE_CONFIG=0` 3. run_hooks “” $sysupgrade_pre_upgrade 这句的意思是:运行函数sysupgrade_pre_upgrade。先介绍下run_hooks函数,定义在文件common.sh中。 ```bash run_hooks() { localarg="$1"; shift forfunc in "$@"; do eval"$func $arg" done } ``` run_hooks函数是钩子函数,其中传过来的第一个参数是函数运行的参数,其余参数为要运行的函数。 ```bash disable_watchdog() { killall watchdog ( ps | grep -v 'grep' | grep '/dev/watchdog' ) && { echo 'Could not disable watchdog' return 1 } } append sysupgrade_pre_upgrade disable_watchdog ``` 就是在升级前,去清除watchdog进程 4. ubus call system upgrade 调用注册到ubus进程的system路径下的update方法,update方法设置了upgrade_running变量值为1,使得在ubus上注册的服务退出时无需等待。 ```bash root@zihome:/usr/sbin# ubus list system -v 'system' @3b46ff0c "board":{} "info":{} "upgrade":{} "watchdog":{"frequency":"Integer","timeout":"Integer","stop":"Boolean"} "signal":{"pid":"Integer","signum":"Integer"} "nandupgrade":{"path":"String"} ``` 5. do_upgrade升级 ```bash if [ -n "$(rootfs_type)" ]; then v "Switching to ramdisk..." (run_ramfs '. /lib/functions.sh; include /lib/upgrade; do_upgrade') & else (do_upgrade) & fi ``` vim /lib/upgrade/common.sh ```bash do_upgrade() { clear_caches v "Performing system upgrade..." if type 'platform_do_upgrade' >/dev/null 2>/dev/null; then platform_do_upgrade "$ARGV" else default_do_upgrade "$ARGV" fi } ``` image升级,保存到对应的mtd分区,这边的PART_NAME在头部会定义为fireware ```bash default_do_upgrade() { sync if [ "$SAVE_CONFIG" -eq 1 ]; then get_image "$1" "$2" | mtd $MTD_CONFIG_ARGS -j "$CONF_TAR" write - "${PART_NAME:-image}" else get_image "$1" "$2" | mtd write - "${PART_NAME:-image}" fi } ``` 6. config保存/删除 保存完固件之后,就开始处理config配置文件 ```bash if [ "$SAVE_CONFIG" -eq 1 ] && type 'platform_copy_config' >/dev/null 2>/dev/null; then platform_copy_config fi if [ "$SAVE_CONFIG" -eq 0 ] && type 'platform_remove_config' >/dev/null 2>/dev/null; then platform_remove_config fi ``` 如果需要保存配置文件,则将config拷贝到/overlay下,`这边的$CONF_TAR压缩包就是上面第二步保存的配置文件 ```bash platform_copy_config() { local board_flash=$(mtk_board_flash) case "$board_flash" in "nand"|"spi") upgrade_log "Save config" rm -rf $1/* cp -af "$CONF_TAR" $1/ sync ;; esac } ``` 如果不需要保存则删除/overlay下的config ```bash platform_remove_config() { local board_flash=$(mtk_board_flash) case "$board_flash" in "nand"|"spi") upgrade_log "Drop config" rm -rf $1/* ;; esac } ``` 7. 重启 ```bash v "Upgrade completed" [ -n "$DELAY" ] && sleep "$DELAY" v "Rebooting system..." upgrade_log_end reboot -f sleep 2 force_reboot ```